package com.bestcem.xm.ticket.service.manager.fsm;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.ObjectId;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.enums.ReqErrorCodeEnum;
import com.bestcem.xm.common.core.enums.ResourceErrorCodeEnum;
import com.bestcem.xm.ticket.constant.Constants;
import com.bestcem.xm.ticket.controller.param.ReopenTicketParam;
import com.bestcem.xm.ticket.dao.TicketDao;
import com.bestcem.xm.ticket.dao.TicketNotifyConfigDao;
import com.bestcem.xm.ticket.entity.mongo.Ticket;
import com.bestcem.xm.ticket.entity.mongo.TicketFlow;
import com.bestcem.xm.ticket.entity.mongo.TicketNotifyConfig;
import com.bestcem.xm.ticket.enums.*;
import com.bestcem.xm.ticket.grpc.client.TicketBaseUserGrpcService;
import com.bestcem.xm.ticket.grpc.client.TicketUserGrpcService;
import com.bestcem.xm.ticket.grpc.client.dto.user.GroupDTO;
import com.bestcem.xm.ticket.grpc.client.dto.user.GroupLevelDTO;
import com.bestcem.xm.ticket.grpc.client.dto.user.RoleDTO;
import com.bestcem.xm.ticket.grpc.client.dto.user.UserDTO;
import com.bestcem.xm.ticket.mq.send.biz.TicketMessageSendService;
import com.bestcem.xm.ticket.mq.send.biz.impl.TicketMessageSendServiceImpl;
import com.bestcem.xm.ticket.service.DeliverAgainService;
import com.bestcem.xm.ticket.service.convert.TicketRecordConvert;
import com.bestcem.xm.ticket.service.convert.TicketRecordMapper;
import com.bestcem.xm.ticket.service.dto.mq.FollowOverdueDTO;
import com.bestcem.xm.ticket.service.manager.notify.TicketNotifyUtil;
import com.bestcem.xm.ticket.service.manager.record.AuditedTicketRecord;
import com.bestcem.xm.ticket.service.manager.record.FollowedTicketRecord;
import com.bestcem.xm.ticket.service.manager.record.ReopenTicketRecord;
import com.bestcem.xm.ticket.service.manager.record.TicketRecordManager;
import com.bestcem.xm.ticket.service.manager.record.param.*;
import com.bestcem.xm.ticket.service.manager.role.TicketAuditor;
import com.bestcem.xm.ticket.service.manager.role.TicketFollower;
import com.bestcem.xm.ticket.service.manager.role.TicketRoleManager;
import com.bestcem.xm.ticket.task.TicketTask;
import com.bestcem.xm.ticket.util.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Nonnull;
import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.common.core.uitls.DateUtil.*;
import static com.bestcem.xm.ticket.service.manager.role.TicketFollower.SUBMIT_SUPER_ADMIN;

/**
 * @author liheng
 * @version v1.0
 * @date 2021/8/23 10:40
 */
@Slf4j
@Service
public class TicketFsmManager {

    @Resource
    private TicketDao ticketDao;
    @Resource
    private TicketNotifyConfigDao ticketNotifyConfigDao;

    @Resource
    private DeliverAgainService deliverAgainService;
    @Resource
    private TicketRecordManager ticketRecordManager;
    /*@Resource
    private TicketMqProductUtil ticketMqProductUtil;*/
    @Resource
    private TicketMessageSendService ticketMessageSendService;
    @Resource
    private TicketNotifyUtil ticketNotifyUtil;
    @Resource
    private TicketTask ticketTask;
    @Resource
    private CommonUtils commonUtils;

    @Resource
    private TicketRecordMapper ticketRecordMapper;
    @Resource
    private TicketRoleManager ticketRoleManager;

    @Resource
    private TicketBaseUserGrpcService ticketBaseUserGrpcService;

    @Resource
    private TicketUserGrpcService ticketUserGrpcService;

//    @Resource
//    private TicketDelayMqSender ticketDelayMqSender;

    @Resource
    private TicketMessageSendServiceImpl ticketMessageService;


    /**
     * 工单流转
     * 根据问卷当前状态和发生的事件, 决定具体业务逻辑
     *
     * @param ticket 工单对象
     * @param event  当前事件
     * @return
     */
    public ServiceResult<String> transfer(Ticket ticket, TicketEventEnum event, @Nonnull TransferParam param) {
        TicketFsm ticketFsm = TicketFsm.getInstance(ticket);

        if (Objects.isNull(ticketFsm)) {
            log.error("[Ticket]工单信息初始化错误,ticket:{}", ticket);
            return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "工单信息为空");
        }
        ticketFsm.setEvent(event.getIndex());

        if (log.isInfoEnabled()) {
            log.info("[Ticket] FSM工具类: {}, 起始状态: {}", ticket.getId(), ticket.getStatus());
            log.info("[Ticket] FSM工具类: {}, 工单事件: {}", ticket.getId(), event);
        }
        Integer status = ticket.getStatus();
        param.setStatus(TicketStatusEnum.getByIndex(status));
        LocalDateTime callTime = LocalDateTime.now();
        // 带纳秒会导致数据库查不到数据,所以置位0
        callTime = callTime.withNano(0);
        TicketStatusEnum statusEnum = TicketStatusEnum.getByIndex(status);
        if (Objects.isNull(statusEnum)) {
            log.error("[Ticket] transfer 工单状态异常,ticketStatus:{}", status);
            return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "工单状态异常");
        }
        switch (statusEnum) {
            case CREATED:
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] CREATED: {} 进入手动创建状态： 触发事件: {},参数：{}", ticketFsm.getTicket().getId(), event.getName(), param);
                }
                ServiceResult<Object> result = handleCreateTicket(ticketFsm, event, param);
                if (!result.isSuccess()) {
                    return ServiceResult.failWithCode(result.getErrorData(), result.getMsg());
                }
                break;
            case FOLLOWING:
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] FOLLOWING: {} 进入跟进状态： 触发事件: {},参数：{}", ticketFsm.getTicket().getId(), event.getName(), param);
                }
                ServiceResult<Object> followResult = follow(ticketFsm, event, param);
                if (!followResult.isSuccess()) {
                    return ServiceResult.failWithCode(followResult.getErrorData(), followResult.getMsg());
                }
                break;
            case AUDITING:
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] AUDITING: {} 进入审核状态： 触发事件: {},参数：{}", ticketFsm.getTicket().getId(), event.getName(), param);
                }
                // 工单2.3暂时没有审核流程
                ServiceResult<Object> auditResult = audit(ticketFsm, event, param);
                if (!auditResult.isSuccess()) {
                    return ServiceResult.failWithCode(auditResult.getErrorData(), auditResult.getMsg());
                }
                break;
            default:
                log.error("[Ticket] transfer 工单状态无法进入工单流程中,ticketStatus:{}", status);
                return ServiceResult.fail("工单状态错误");
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket]TRANSFER: {}, 结束状态: {} ", ticket.getId(), ticket.getStatus());
            log.info("[Ticket]ticket: {}", ticketFsm.getTicket());
        }
        Ticket changedTicket = ticketFsm.getTicket();
        changedTicket = ticketDao.overrideUpdateTicket(changedTicket);
        if (Objects.isNull(changedTicket)) {
            // ？？是否要回滚工单记录
            return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "保存工单失败");
        }

        // 发送mq消息 redis_mq.publish(ticket_changed, mq_data)
        ticketMessageSendService.publishTicketChangeMq(changedTicket);

        // 工单转状态改变给联系人足迹发送MQ
        if (StringUtils.isNotBlank(changedTicket.getMemberId())) {
            // 工单转状态改变给联系人足迹发送MQ
            // 手动创建工单会有多个memberId的情况,与蔡霖确认每个memberId都同步足迹点
            ticketMessageSendService.publishMemberFootSaveMqRefactor(changedTicket);
        }
        if (TicketStatusEnum.FOLLOWING.getIndex().equals(status) && TicketEventEnum.COMMIT_FOLLOWED.equals(event)) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 提交跟进记录，call_time={}", callTime);
            }
            if (!TicketOperationTypeEnum.MANUAL.getIndex().equals(ticket.getOperationType())) {
                deliverAgainService.validTicketAndDeliverAgain(changedTicket, 1, callTime);
            }
        } else if (!Objects.equals(status, changedTicket.getStatus())) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 工单状态发生变化，初始状态={},改变之后的状态={}", status, changedTicket.getStatus());
            }
            if (!TicketOperationTypeEnum.MANUAL.getIndex().equals(ticket.getOperationType())) {
                // 过滤工单自动创建的二次投放检查
                if (!TicketStatusEnum.CREATED.getIndex().equals(status)) {
                    deliverAgainService.validTicketAndDeliverAgain(changedTicket, 0, null);
                }
            }
        }
        return ServiceResult.success(changedTicket.getId());
    }


    /**
     * 工单转派
     *
     * @param ticket 工单信息
     * @param param  转派参数
     * @return 工单id
     * @author liheng
     * @author XiaoYunSong <ys.xiao@idiaoyan.com> 工单对接钉钉需求, 新增组织转派
     * @date 2021/12/3 10:41
     */
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ServiceResult<String> transferTicket(Ticket ticket, TransferTicketRecordParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单转派 {}; 起始状态 {}", ticket.getId(), ticket.getStatus());
        }

        // 工单关闭状态不可以转派
        if (TicketStatusEnum.CLOSED.getIndex().equals(ticket.getStatus())) {
            log.error("[Ticket] 工单转派 {} 工单关闭状态不可以转派", ticket.getId());
            return ServiceResult.fail(ticket.getId(), "工单关闭状态不可以转派");
        }

        // 工单对接钉钉需求, 转派时把工单的组织换成被转派对象的组织
        ticket.setRegion(param.getReceiveGroupId());
        ticket.setRegionName(param.getReceiveGroupName());

        TicketFsm instance = TicketFsm.getInstance(ticket);
        if (Objects.isNull(instance)) {
            log.error("[Ticket] 工单信息初始化错误; Ticket:{}", ticket);
            return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "工单信息为空");
        }

        // 组织转派, 把接收人的名字改为所有接收用户的名字用 、 拼起来, 把接收人的id改为所有接收用户的id用 、 拼起来
        if (FollowerFilterEnum.REGION.getIndex().equals(param.getFollowFilter())) {
            String receiveUsername = Constants.BLANK, receiveUserId = Constants.BLANK;
            List<UserDTO> userDTOs = ticketUserGrpcService.listUsersByRole(param.getReceiveRoleId(),
                    param.getReceiveGroupId(), param.getReceiveGroupLevelId());
            if (!CollectionUtils.isEmpty(userDTOs)) {
                receiveUserId = userDTOs.stream().map(UserDTO::getId).collect(Collectors.joining("、"));
                receiveUsername = userDTOs.stream().map(UserDTO::getName).collect(Collectors.joining("、"));
            }
            param.setReceiveUserId(receiveUserId);
            param.setReceiveUsername(receiveUsername);
        }
        // 保存 转派的TicketRecord 数据
        ticketRecordManager.transfer(param);


        // 工单转派后续的跟进人处理
        // 1、修改工单跟进人
        TicketFlow ticketFlow = instance.getTicketFlow();
        // 根据参数构造新的跟进配置
        TicketFlow.FollowConfig followConfig = getFollowConfig(param);
        TicketFollower follower = TicketFollower.getInstance(followConfig);
        instance.setFirstFollower(follower);
        // 设置当前跟进节点为第一节点
        instance.getTicket().setCurrentFollowNode(1);
        // 新的工单跟进配置更新进ticket记录
        ticketFlow.setFollowConfig(Arrays.asList(followConfig));
        instance.getTicket().setTicketFlow(ticketFlow);
        instance.setTicketFlow(ticketFlow);
        if (ticketRoleManager.hasUser(instance.getFirstFollower(), instance.getTicket().getRegion())) {
            dispatchFollower(1, TicketEventEnum.TRANSFER, instance);
        } else if (TicketFollower.SUBMIT_SUPER_ADMIN.equals(instance.getFirstFollower().getEmptyFollowerHandle())) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 工单转派 第一跟进人为空，转派给系统管理员，第一跟进人信息：{}", instance.getFirstFollower());
            }
            //  转派给系统管理员
            List<UserDTO> userDTOS = commonUtils.getOrgAllAdminUser(param.getOrgId());
            if (CollectionUtils.isEmpty(userDTOS)) {
                log.error("[Ticket] 工单转派 查询超级管理员用户返回为空，orgId {}", param.getOrgId());
                return ServiceResult.failWithCode(ResourceErrorCodeEnum.NOT_FOUND, "未找到超级管理员");
            }
            // 与python确认，超级管理员只有一个
            UserDTO userDTO = userDTOS.get(0);
            // 将工单的组织变成为超级管理员的组织
            List<String> ancestorIds = ticketUserGrpcService.listAncestorIdsByUserId(userDTO.getId());
            instance.getTicket().setRegion("");
            instance.getTicket().setRegionName("");
            if (!CollectionUtils.isEmpty(ancestorIds)) {
                GroupDTO group = ticketBaseUserGrpcService.getGroup(ancestorIds.get(0));
                if (Objects.nonNull(group) && ObjectId.isValid(group.getId())) {
                    instance.getTicket().setRegion(group.getId());
                    instance.getTicket().setRegionName(group.getTitle());
                }
            }

            TicketFlow.FollowConfig adminFollowConfig = new TicketFlow.FollowConfig();
            adminFollowConfig.setFollowerId(userDTO.getId());
            adminFollowConfig.setFollowerName(userDTO.getName());
            adminFollowConfig.setFollowerFilter(FollowerFilterEnum.USER.getIndex());

            // 修改工单中跟进人为超级管理员
            TicketFlow adminTicketFlow = instance.getTicket().getTicketFlow();
            List<TicketFlow.FollowConfig> adminFollowConfigs = Collections.singletonList(adminFollowConfig);
            adminTicketFlow.setFollowConfig(adminFollowConfigs);
            // 修改第一跟进人
            TicketFollower adminFollower = TicketFollower.getInstance(adminFollowConfig);
            instance.setFirstFollower(adminFollower);

            // 转换参数对象
            SystemTicketRecordParam systemParam = new SystemTicketRecordParam();
            systemParam.setOrgId(instance.getTicket().getOrgId());
            systemParam.setTicketId(instance.getTicket().getId());
            systemParam.setStatus(TicketStatusEnum.getByIndex(instance.getTicket().getStatus()));
            systemParam.setType(instance.getTicketType());
            systemParam.setUserName(param.getUserName());
            systemParam.setUserId(param.getUserId());
            systemParam.setRegionName(param.getRegionName());
            systemParam.setRegionId(param.getRegionId());
            systemParam.setFollowerInfo(userDTO.getName());
            // 记录转派给超级管理员日志
            ticketRecordManager.system(TicketEventEnum.TRANSFER_FOLLOW, systemParam, ticket);
            dispatchFollower(1, TicketEventEnum.TRANSFER, instance);
            instance.getTicket().setCurFollowUserId(this.getCurrentFollowUser(instance));
        }

        // 转派后就没有第二跟进人和逾期时间了
        instance.setSecondFollower(null);
        instance.getTicket().setCurFollowUserId(getCurrentFollowUser(instance));

        // 工单变更保存
        Ticket changedTicket = instance.getTicket();
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单转派-工单最终结果 {}", changedTicket);
        }
        changedTicket = ticketDao.overrideUpdateTicket(changedTicket);

        if (Objects.isNull(changedTicket)) {
            // ???是否要回滚工单记录
            log.error("[Ticket] 工单转派 工单保存失败");
            return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "保存工单失败");
        }

        // 发送工单修改消息给BI， redis_mq.publish(ticket_changed, mq_data), 手动创建的工单没有关联问卷, 不发送MQ
        if (!TicketOperationTypeEnum.MANUAL.getIndex().equals(changedTicket.getOperationType())) {
            ticketMessageSendService.publishTicketChangeMq(changedTicket);
        }

        // 转派无需二次投放
        return ServiceResult.success(ticket.getId());
    }


    /**
     * 重新打开工单,工单必须为关闭状态
     *
     * @param ticket
     * @param event
     * @param param
     * @return
     */
    public ServiceResult<String> reopenTicket(Ticket ticket, TicketEventEnum event, ReopenTicketParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket]工单重新打开: {}, 起始状态: {}", ticket.getId(), ticket.getStatus());
            log.info("[Ticket]工单重新打开: {}, 工单事件: {}", ticket.getId(), event);
        }

        if (TicketStatusEnum.MANUAL_CLOSED.getIndex().equals(ticket.getStatus())
                || TicketStatusEnum.SYSTEM_CLOSED.getIndex().equals(ticket.getStatus())
                || TicketStatusEnum.CLOSED.getIndex().equals(ticket.getStatus())) {
            // 如果是关闭状态才可以重新打开工单

            TicketFsm instance = TicketFsm.getInstance(ticket);

            if (Objects.isNull(instance)) {
                log.error("[Ticket]工单信息初始化错误,ticket:{}", ticket);
                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "工单信息为空");
            }
            instance.setEvent(event.getIndex());

            // 记录重新打开工单记录
            ReopenTicketRecordParam recordParam = changeToReopenParam(ticket, param);

            // 保存 TicketRecord 数据
            ReopenTicketRecord reopenTicketRecord = ticketRecordManager.reopenRecord(recordParam);

            SystemTicketRecordParam systemParam = new SystemTicketRecordParam();
            // 无需跟进处理，需要查看是否有 通知管理员 和 抄送人信息
            if (TicketTypeEnum.UN_NEED_DEAL.equals(instance.getTicketType())) {
                systemParam.setStatus(TicketStatusEnum.UNNEED_DEAL);
                systemParam.setType(TicketTypeEnum.UN_NEED_DEAL);
                this.unNeedDeal(systemParam, instance);
                return ServiceResult.success();
            }

            // 判断工单没有归属，没有层级，所以不存在跟进人需要关闭
//            if (StringUtils.isBlank(instance.getTicket().getRegion()) && TicketTTypeEnum.NEED_DEAL.equals(instance.getTicketType())) {
//                systemParam.setOrgId(instance.getTicket().getOrgId());
//                systemParam.setTicketId(instance.getTicket().getId());
//                systemParam.setType(instance.getTicketType());
//                systemParam.setStatus(TicketStatusEnum.CLOSED);
//                this.systemClosed(systemParam, instance);
//                return ServiceResult.success();
//            }

            // 修改工单状态为跟进中
            ticket.setStatus(TicketStatusEnum.FOLLOWING.getIndex());

            // 重新分配跟进人
            if (Integer.valueOf(1).equals(instance.getTicket().getCurrentFollowNode())) {
                this.dispatchFollower(1, TicketEventEnum.REOPEN, instance);
            } else if (Integer.valueOf(2).equals(instance.getTicket().getCurrentFollowNode())) {
                this.dispatchFollower(2, TicketEventEnum.REOPEN, instance);
            }

            // 记录结束的状态
            if (log.isInfoEnabled()) {
                log.info("[Ticket]TRANSFER: {}, 结束状态: {} ", ticket.getId(), ticket.getStatus());
                log.info("[Ticket]ticket: {}", instance.getTicket());
            }
            // 保存工单修改
            Ticket changedTicket = instance.getTicket();
            changedTicket = ticketDao.overrideUpdateTicket(changedTicket);
            if (Objects.isNull(changedTicket)) {
                //  todo: ？？是否要回滚工单记录
                return ServiceResult.failWithCode(ReqErrorCodeEnum.PARAM_FORMAT, "保存工单失败");
            }

            // 发送mq消息 redis_mq.publish(ticket_changed, mq_data)
            ticketMessageSendService.publishTicketChangeMq(changedTicket);
            // 重新打开工单需要同步足迹点
            ticketMessageSendService.publishMemberFootSaveMqRefactor(changedTicket);

        } else {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 工单状态不是关闭中，ticketId:{},ticketStatus:{}", param.getTicketId(), ticket.getStatus());
            }
        }
        return ServiceResult.success(ticket.getId());
    }


    /**
     * 生成重新打开工单日志参数
     *
     * @param ticket
     * @param param
     * @return
     */
    private ReopenTicketRecordParam changeToReopenParam(Ticket ticket, ReopenTicketParam param) {
        ReopenTicketRecordParam recordParam = new ReopenTicketRecordParam();

        recordParam.setTicketId(ticket.getId());
        recordParam.setOrgId(param.getOrgId());
        // 重新打开的原因
        recordParam.setReason(param.getReason());
        recordParam.setRoleId(ticket.getFollowRoleId());

        recordParam.setUserName(param.getUserName());

        recordParam.setUserId(param.getUserId());
        recordParam.setStatus(TicketStatusEnum.getByIndex(ticket.getStatus()));
        recordParam.setRegionName(ticket.getRegionName());
        return recordParam;

    }


    /**
     * 创建工单逻辑
     *
     * @param ticketFsm 流转类
     * @param event     触发事件
     * @return ServiceResult<Object>
     * @author liheng
     * @date 2021/9/10 13:07
     **/
    private ServiceResult<Object> handleCreateTicket(TicketFsm ticketFsm, TicketEventEnum event, TransferParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] CREATED: {} 进入创建状态 触发事件: {}", ticketFsm.getTicket().getId(), event.getName());
        }
        if (TicketEventEnum.USER_CREATED.equals(event)) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: {} 进入手动创建状态： 触发事件: {}", ticketFsm.getTicket().getId(), event.getName());
            }
            return handleUserCreateTicket(ticketFsm, event, param);
        }
        // 如果事件不是系统创建和用户创建
        if (!TicketEventEnum.SYSTEM_CREATED.equals(event)) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: 事件不是系统创建和用户创建");
            }
            return ServiceResult.success();
        }
        SystemTicketRecordParam systemParam = new SystemTicketRecordParam();
        systemParam.setOrgId(ticketFsm.getTicket().getOrgId());
        systemParam.setTicketId(ticketFsm.getTicket().getId());
        systemParam.setStatus(TicketStatusEnum.FOLLOWING);
        systemParam.setType(ticketFsm.getTicketType());
        // 创建工单创建记录
        if (log.isInfoEnabled()) {
            log.info("[Ticket] CREATED: 记录工单创建日志");
        }
        ticketRecordManager.system(event, systemParam, ticketFsm.getTicket());
        // 设置状态为跟进中
        ticketFsm.getTicket().setStatus(TicketStatusEnum.FOLLOWING.getIndex());
        // 无需跟进处理，需要查看是否有 通知管理员 和 抄送人信息
        if (TicketTypeEnum.UN_NEED_DEAL.equals(ticketFsm.getTicketType())) {
            systemParam.setStatus(TicketStatusEnum.UNNEED_DEAL);
            this.unNeedDeal(systemParam, ticketFsm);
            return ServiceResult.success();
        }
        // 工单没有归属，没有层级，所以不存在跟进人需要关闭
        if (StringUtils.isBlank(ticketFsm.getTicket().getRegion()) && TicketTypeEnum.NEED_DEAL.equals(ticketFsm.getTicketType())) {
            systemParam.setStatus(TicketStatusEnum.CLOSED);
            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: {} 工单没有归属，没有层级，所以不存在跟进人需要关闭", ticketFsm.getTicket().getId());
            }
            this.systemClosed(systemParam, ticketFsm);
            return ServiceResult.success();
        }
        // 工单存在第一跟进人, 交给第一个跟进人处理, 看第一跟进人角色中是否有用户
        if (Objects.nonNull(ticketFsm.getFirstFollower()) && ticketRoleManager.hasUser(ticketFsm.getFirstFollower(), ticketFsm.getTicket().getRegion())) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: 第一跟进人:{},", ticketFsm.getFirstFollower());
            }
            ticketFsm.getTicket().setCurrentFollowNode(1);
            // 提供给第一跟进人
            this.dispatchFollower(1, event, ticketFsm);
        } else {
            // 第一跟进人为空, 且空跟进人处理是系统关闭
            if (TicketFollower.SYSTEM_CLOSE.equals(ticketFsm.getFirstFollower().getEmptyFollowerHandle())) {
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] CREATED: {} 第一跟进人为空且处理为系统关闭", ticketFsm.getTicket().getId());
                }
                systemClosed(systemParam, ticketFsm);
            } else if (TicketFollower.TRANSFER_SECOND_FOLLOWER.equals(ticketFsm.getFirstFollower().getEmptyFollowerHandle())) {
                // 第一跟进人为空, 且空跟进人处理是转到第二跟进人
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] CREATED: {} 第一跟进人为空且处理为转给第二跟进人", ticketFsm.getTicket().getId());
                }
                // 第二跟进人存在, 转到第二跟进人跟进
                if (Objects.nonNull(ticketFsm.getSecondFollower()) && ticketRoleManager.hasUser(ticketFsm.getSecondFollower(), ticketFsm.getTicket().getRegion())) {
                    ticketFsm.getTicket().setCurrentFollowNode(2);
                    dispatchFollower(2, event, ticketFsm);
                } else {
                    // 第二跟进人也为空时, 默认系统关闭工单
                    if (log.isInfoEnabled()) {
                        log.info("[Ticket] CREATED: {} 第二跟进人为空系统关闭", ticketFsm.getTicket().getId());
                    }
                    this.systemClosed(systemParam, ticketFsm);
                }
            }
        }
        return ServiceResult.success();
    }


    /**
     * 处理用户手动创建工单
     *
     * @param ticketFsm 工单流转参数
     * @param event     工单流转事件
     * @return ServiceResult<Object> 返回值
     * @author jy.zhao
     * @date 2021/9/10 15:43
     **/
    private ServiceResult<Object> handleUserCreateTicket(TicketFsm ticketFsm, TicketEventEnum event, TransferParam param) {
        SystemTicketRecordParam systemParam = new SystemTicketRecordParam();
        systemParam.setOrgId(ticketFsm.getTicket().getOrgId());
        systemParam.setTicketId(ticketFsm.getTicket().getId());
        systemParam.setStatus(TicketStatusEnum.FOLLOWING);
        systemParam.setType(ticketFsm.getTicketType());
        systemParam.setUserName(param.getUserName());
        systemParam.setOrgId(param.getOrgId());
        systemParam.setUserId(param.getUserId());
        systemParam.setRegionId(ticketFsm.currentFollower().getRegion());

//        systemParam.setStatus(TicketStatusEnum.getByIndex(ticketFsm.getTicket().getStatus()));
        // 创建工单创建记录
        if (log.isInfoEnabled()) {
            log.info("[Ticket] CREATED: 记录手动创建工单日志");
        }

        ticketRecordManager.system(event, systemParam, ticketFsm.getTicket());
        // 无需跟进处理，需要查看是否有 通知管理员 和 抄送人信息
        if (TicketTypeEnum.UN_NEED_DEAL.equals(ticketFsm.getTicketType())) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: 手动创建工单-工单没无需处理");
            }
            systemParam.setStatus(TicketStatusEnum.UNNEED_DEAL);
            systemParam.setType(TicketTypeEnum.UN_NEED_DEAL);
            this.unNeedDeal(systemParam, ticketFsm);
            return ServiceResult.success();
        }
        // 不是手动创建的工单，才进行下面的判断。 工单没有归属，没有层级，所以不存在跟进人需要关闭
//        if ((!TicketOperationTypeEnum.MANUAL.getIndex().equals(ticketFsm.getTicket().getOperationType()))
//                && StringUtils.isBlank(ticketFsm.getTicket().getRegion()) && TicketTTypeEnum.NEED_DEAL.equals(ticketFsm.getTicketType())) {
//            if (log.isInfoEnabled()) {
//                log.info("[Ticket] CREATED: 手动创建工单-工单没有层级自动关闭");
//            }
//            systemParam.setStatus(TicketStatusEnum.CLOSED);
//            this.systemClosed(systemParam, ticketFsm);
//            return ServiceResult.success();
//        }

        // 设置状态为跟进中
        ticketFsm.getTicket().setStatus(TicketStatusEnum.FOLLOWING.getIndex());

        // 工单存在第一跟进人, 交给第一个跟进人处理, 看第一跟进人角色中是否有用户
        if (Objects.nonNull(ticketFsm.getFirstFollower()) && ticketRoleManager.hasUser(ticketFsm.getFirstFollower(), ticketFsm.getTicket().getRegion())) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: 手动创建工单-转派第一跟进人");
            }
            // 提供给第一跟进人
            this.dispatchFollower(1, event, ticketFsm);
        } else if (TicketFollower.SUBMIT_SUPER_ADMIN.equals(ticketFsm.getFirstFollower().getEmptyFollowerHandle())) {

            log.error("[ticket]: 手动创建工单：第一跟进人为空，转派给系统管理员，第一跟进人信息：{}", ticketFsm.getFirstFollower());
            List<UserDTO> userDTOS = commonUtils.getOrgAllAdminUser(param.getOrgId());
            if (CollUtil.isEmpty(userDTOS)) {
                log.error("[ticket]: 手动创建工单；查询超级管理员用户返回为空，orgId：{},返回的用户信息：{}", param.getOrgId(), userDTOS);
                return ServiceResult.failWithCode(ResourceErrorCodeEnum.NOT_FOUND, "未找到超级管理员");
            }

            // 与python确认，超级管理员只有一个
            UserDTO userDTO = userDTOS.get(0);
            TicketFlow.FollowConfig followConfig = new TicketFlow.FollowConfig();
            followConfig.setFollowerId(userDTO.getId());
            followConfig.setFollowerName(userDTO.getName());
            followConfig.setFollowerFilter(FollowerFilterEnum.USER.getIndex());


            // 修改工单中跟进人为超级管理员
            TicketFlow ticketFlow = ticketFsm.getTicket().getTicketFlow();
            List<TicketFlow.FollowConfig> followConfigs = Collections.singletonList(followConfig);
            ticketFlow.setFollowConfig(followConfigs);
            // 修改第一跟进人
            TicketFollower adminFollower = TicketFollower.getInstance(followConfig);
            ticketFsm.setFirstFollower(adminFollower);

            if (log.isInfoEnabled()) {
                log.info("[Ticket] CREATED: 手动创建工单-转派给超级管理员");
            }

            systemParam.setFollowerInfo(userDTO.getName());
            // 记录转派给超级管理员日志
            ticketRecordManager.system(TicketEventEnum.TRANSFER_FOLLOW, systemParam, ticketFsm.getTicket());
            if (log.isInfoEnabled()) {
                log.info("[ticket] 工单逾期有第二跟进人时创建转交记录");
            }
            this.dispatchFollower(1, TicketEventEnum.TRANSFER, ticketFsm);
            ticketFsm.getTicket().setCurFollowUserId(this.getCurrentFollowUser(ticketFsm));
//            ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentFollower(), TicketEventEnum.FOLLOWING);
        }


        return ServiceResult.success();
    }


    /**
     * 跟进工单逻辑
     *
     * @param ticketFsm
     * @param event
     * @param param
     */
    private ServiceResult<Object> follow(TicketFsm ticketFsm, TicketEventEnum event, TransferParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] FOLLOW: {} 进入跟进状态 触发事件: {}", ticketFsm.getTicket().getId(), event.getName());
        }
        Integer status;
        switch (event) {
            case COMMIT_FOLLOWED:
                // 获取有权限跟进工单的用户
                List<UserDTO> users = ticketTask.getHasPermUserByFollower(ticketFsm.getTicket(), ticketFsm.currentFollower());
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] FOLLOW: 工单有权限跟进用户：{}, 工单当前跟进人：{}", JSONObject.toJSONString(users),
                            ticketFsm.currentFollower());
                }
                String userId = param.getUserId();
                List<Object> collect = users.stream().filter(o -> o.getId().equals(userId)).collect(Collectors.toList());
                // 如果当前用户不在可跟进用户list中
                if (CollUtil.isEmpty(collect)) {
                    log.error("该用户无权限跟进工单,userId:{}", userId);
                    return ServiceResult.failWithCode(ResourceErrorCodeEnum.PERMISSION_DENY, "该用户无权限跟进工单");
                }
                FollowedTicketRecordParam followedTicketRecordParam = TicketRecordConvert.paramToFollow(param);
                // 获取跟进记录中的工单最终状态
                status = ticketFsm.getTicket().getStatus();
                // 工单2.2 P2 中 提交跟进记录 和 关闭工单 拆分
                followedTicketRecordParam.setStatus(TicketStatusEnum.getByIndex(status));
                followedTicketRecordParam.setType(ticketFsm.getTicketType());
                FollowedTicketRecord followed = ticketRecordManager.followed(followedTicketRecordParam, ticketFsm.getFollowedTemplate());
                if (Objects.isNull(followed)) {
                    log.error("[Ticket] com.bestcem.xm.ticket.service.util.record.TicketRecordUtil.followed " +
                            "返回null，param：{}，template:{}", followedTicketRecordParam, ticketFsm.getFollowedTemplate());
                    return ServiceResult.failWithCode(ResourceErrorCodeEnum.NOT_FOUND, "工单跟进返回null， ticketRecordUtil.followed()返回null");
                }
                // 提交跟进记录后，改变为True
                ticketFsm.getTicket().setHasCommit(true);
                // 工单2.2p2 取消了 is_completed字段，提交跟进记录后，就记录处理人
                List<String> followUserIds = ticketFsm.getTicket().getFollowUserIds();
                followUserIds = Optional.ofNullable(followUserIds).orElse(new ArrayList<>(10));
                ticketFsm.getTicket().setFollowUserIds(followUserIds);

                if (!followUserIds.contains(followed.getUserId())) {
                    // 存在多次提交跟进的情况, 同一个人只添加一次
                    // 提交记录的用户加入跟进者中
                    ticketFsm.getTicket().getFollowUserIds().add(followed.getUserId());
                }
                // 跟进已完成, 转移到审核步骤,2.3没有审核了
                break;
            case FOLLOW_OVERDUE_REMIND:
                // 发送通知
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] FOLLOW: 跟进通知逾期提醒发送消息：ticket：{}, 工单当前跟进人：{}，事件event：{}", JSONObject.toJSONString(ticketFsm.getTicket()),
                            ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                }
                ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                break;
            case FOLLOW_OVERDUE:
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] FOLLOW: 工单跟进已逾期提醒发送消息：ticket：{}, 工单当前跟进人：{}，事件event：{}", JSONObject.toJSONString(ticketFsm.getTicket()),
                            ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                }
                status = ticketFsm.getTicket().getStatus();
                ticketFsm.getTicket().setHasFollowOverdue(true);
                param.setTicketId(ticketFsm.getTicket().getId());
                param.setStatus(TicketStatusEnum.getByIndex(status));
                param.setOrgId(ticketFsm.getTicket().getOrgId());
                param.setRegionName(ticketFsm.getTicket().getRegionName());
                param.setType(TicketTypeEnum.getByIndex(ticketFsm.getTicket().getType()));
                SystemTicketRecordParam systemTicketRecordParam = ticketRecordMapper.paramToSystem(param);
                // 记录逾期记录
                ticketRecordManager.system(event, systemTicketRecordParam, ticketFsm.getTicket());
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] FOLLOW: 工单跟进已逾期提醒发送消息：ticket：{}, 工单当前跟进人：{}，事件event：{}", JSONObject.toJSONString(ticketFsm.getTicket()),
                            ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                }
                // 发送跟进逾期通知
                ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));

                if (ticketFsm.getTicket().getCurrentFollowNode() == 1) {
                    if (log.isInfoEnabled()) {
                        log.info("[Ticket] FOLLOW: 第一个跟进人跟进逾期：ticket：{}, 工单当前跟进人：{}，事件event：{}", JSONObject.toJSONString(ticketFsm.getTicket()),
                                ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                    }
                    // 第一个跟进人跟进逾期
                    if (TicketFollower.TRANSFER_SECOND_FOLLOWER.equals(ticketFsm.getFirstFollower().getOverdueHandle())) {
                        // 逾期处理转交第二人
                        if (log.isInfoEnabled()) {
                            log.info("[ticket] 工单逾期转交个第二跟进人,ticketId:{},第二跟进人:{}", ticketFsm.getTicket().getId(), ticketFsm.getSecondFollower());
                        }
                        // 转交给第二跟进人之前需要判断第二跟进人是否为空
                        if (!(Objects.nonNull(ticketFsm.getSecondFollower()) && ticketRoleManager.hasUser(ticketFsm.getSecondFollower(), ticketFsm.getTicket().getRegion()))) {
                            if (Objects.isNull(ticketFsm.getSecondFollower().getOverdueHandle())) {
                                if (log.isInfoEnabled()) {
                                    log.info("[ticket] 工单逾期，转派给第二跟进人，第二跟进人逾期处理为空,ticketId:{}，SecondFollower().getOverdueHandle():{}", ticketFsm.getTicket().getId(), ticketFsm.getSecondFollower().getOverdueHandle());
                                }
                            }
                            if (log.isInfoEnabled()) {
                                log.info("[ticket] 工单逾期，转派给第二跟进人，查询不到跟进人,ticketId:{}", ticketFsm.getTicket().getId());
                            }
                            if (TicketStatusEnum.CLOSED.getIndex().equals(status)) {
                                systemTicketRecordParam.setForbiddenPublish(true);
                            }
                            this.systemClosed(systemTicketRecordParam, ticketFsm);
                        } else {
                            if (log.isInfoEnabled()) {
                                log.info("[ticket] 工单逾期转交个第二跟进人,ticketId:{}", ticketFsm.getTicket().getId());
                            }
                            ticketFsm.getTicket().setCurrentFollowNode(2);
                            ticketFsm.getTicket().setHasFollowOverdue(false);
                            this.dispatchFollower(2, event, ticketFsm);
                            //  拼接转交第二跟进人的用户信息  主要是为了在记录中拼接 title 工单
                            if (FollowerFilterEnum.REGION.equals(ticketFsm.getSecondFollower().getFollowerFilter())) {
                                if (log.isInfoEnabled()) {
                                    log.info("[ticket] 工单逾期，转派给第二跟进人，第二跟进人为按层级转派,ticketId:{}", ticketFsm.getTicket().getId());
                                }
                                String region = ticketFsm.getSecondFollower().getRegion();
                                String roleId = ticketFsm.getSecondFollower().getRoleId();
                                // 获取层级
                                GroupLevelDTO groupLevel = ticketBaseUserGrpcService.getGroupLevel(region);

                                // 获取获取跟进人角色信息
                                RoleDTO role = ticketBaseUserGrpcService.getRole(roleId);
                                if (Objects.nonNull(groupLevel) && Objects.nonNull(role)) {
                                    // 跟进人信息
                                    String followerInfo = groupLevel.getTitle() + "-" + role.getTitle();
                                    systemTicketRecordParam.setFollowerInfo(followerInfo);
                                }

                            } else {
                                if (log.isInfoEnabled()) {
                                    log.info("[ticket] 工单逾期，转派给第二跟进人，第二跟进人为按用户转派,ticketId:{}", ticketFsm.getTicket().getId());
                                }
                                String followerId = ticketFsm.getSecondFollower().getFollowerId();
                                // 查询跟进人姓名
                                UserDTO user = ticketBaseUserGrpcService.getUser(followerId);
                                // 如果查询到users
                                if (Objects.nonNull(user)) {
                                    systemTicketRecordParam.setFollowerInfo(user.getName());
                                }
                                if (log.isInfoEnabled()) {
                                    log.info("[ticket] 工单逾期有第二跟进人时创建转交记录");
                                }
                            }
                            ticketRecordManager.system(TicketEventEnum.TRANSFER_FOLLOW, systemTicketRecordParam, ticketFsm.getTicket());
                        }
                    } else if (TicketFollower.KEEP_FOLLOW.equals(ticketFsm.getFirstFollower().getOverdueHandle())) {
                        if (log.isInfoEnabled()) {
                            log.info("[ticket] 工单逾期，逾期继续跟进,ticketId:{}", ticketFsm.getTicket().getId());
                        }
                        // 逾期继续跟进
                        ticketFsm.getTicket().setHasFollowOverdue(false);
                    } else if (TicketFollower.SYSTEM_CLOSE.equals(ticketFsm.getFirstFollower().getOverdueHandle())) {
                        if (log.isInfoEnabled()) {
                            log.info("[ticket] 工单逾期，逾期系统关闭,ticketId:{}", ticketFsm.getTicket().getId());
                        }
                        // 逾期系统关闭
                        if (TicketStatusEnum.CLOSED.getIndex().equals(status)) {
                            systemTicketRecordParam.setForbiddenPublish(true);
                        }
                        this.systemClosed(systemTicketRecordParam, ticketFsm);

                    }
                } else if (ticketFsm.getTicket().getCurrentFollowNode() == 2) {
                    // 第二个跟进人跟进逾期
                    if (TicketFollower.KEEP_FOLLOW.equals(ticketFsm.getSecondFollower().getOverdueHandle())) {
                        if (log.isInfoEnabled()) {
                            log.info("[Ticket] FOLLOW: 第二个跟进人跟进逾期：ticket：{}, 工单当前跟进人：{}，事件event：{}", JSONObject.toJSONString(ticketFsm.getTicket()),
                                    ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                        }
                        // 逾期继续跟进
                        ticketFsm.getTicket().setHasFollowOverdue(false);
                    } else if (TicketFollower.SYSTEM_CLOSE.equals(ticketFsm.getSecondFollower().getOverdueHandle())) {
                        if (log.isInfoEnabled()) {
                            log.info("[Ticket] FOLLOW: 逾期系统关闭：ticket：{}, 工单当前跟进人：{}，事件event：{}", JSONObject.toJSONString(ticketFsm.getTicket()),
                                    ticketFsm.currentFollower(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                        }
                        // 逾期系统关闭
                        if (TicketStatusEnum.CLOSED.getIndex().equals(status)) {
                            systemTicketRecordParam.setForbiddenPublish(true);
                        }
                        this.systemClosed(systemTicketRecordParam, ticketFsm);
                    }

                }
                break;
            case MANUAL_CLOSED:
                // 手动关闭时间处理
                SystemTicketRecordParam ticketRecordParam = ticketRecordMapper.paramToSystem(param);
                this.manualClose(ticketRecordParam, ticketFsm);
                break;
            default:
                break;
        }
        return ServiceResult.success();
    }


    /**
     * 转派工单处理，修改跟进配置中的跟进人，去除第二跟进人，其它属性不修改
     *
     * @param param 新的跟进配置参数
     */
    private TicketFlow.FollowConfig getFollowConfig(TransferTicketRecordParam param) {
        TicketFlow.FollowConfig followConfig = new TicketFlow.FollowConfig();
        followConfig.setFollowerFilter(param.getFollowFilter());
        if (FollowerFilterEnum.REGION.getIndex().equals(param.getFollowFilter())) {
            // 设置区域id (levelId) 和角色id
            followConfig.setRoleId(param.getReceiveRoleId());
            followConfig.setRegion(param.getReceiveGroupLevelId());
            followConfig.setFollowerFilter(FollowerFilterEnum.REGION.getIndex());
        } else if (FollowerFilterEnum.USER.getIndex().equals(param.getFollowFilter())) {
            followConfig.setFollowerId(param.getReceiveUserId());
            followConfig.setFollowerName(param.getReceiveUsername());
            followConfig.setFollowerFilter(FollowerFilterEnum.USER.getIndex());
        }
        // 转派工单中，新的跟进人不添加跟进时限
        followConfig.setTimeLimit(null);
        followConfig.setTimeLimitType(null);
        // 设置跟进人为空，则转派给超级管理员
        followConfig.setEmptyFollowerHandle(SUBMIT_SUPER_ADMIN);
        return followConfig;
    }


    /**
     * 审核状态
     * 如果只有一个审核节点  那个该节点作为最后一个节点 显示处理措施 ；
     * 如果有两个审核节点，第一个作为1级审核，无处理措施，同意或者不同意工单状态都为审核中，
     * 第二节点有处理措施，可以选择处理结果，工单状态根据处理结果反馈改变
     *
     * @deprecated 工单2.3已经没有审核了, 这里不做任何处理
     */
    @Deprecated
    private ServiceResult<Object> audit(TicketFsm ticketFsm, TicketEventEnum event, TransferParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] AUDITING: {} 进入审核状态 触发事件: {}", ticketFsm.getTicket().getId(), event);
        }
        // 提交审核记录
        switch (event) {
            case COMMIT_AUDITED:
                // 获取有权限跟进工单的用户
                List<UserDTO> users = ticketTask.getHasPermUserByFollower(ticketFsm.getTicket(), ticketFsm.currentFollower());
                String userId = param.getUserId();
                List<Object> collect = users.stream().filter(o -> o.equals(userId)).collect(Collectors.toList());
                // 如果当前用户不在可跟进用户list中
                if (CollUtil.isEmpty(collect)) {
                    log.error("该用户无权限跟进工单,userId:{}", userId);
                    return ServiceResult.failWithCode(ResourceErrorCodeEnum.PERMISSION_DENY, "用户无权跟进工单");
                }
                Integer status = ticketFsm.getTicket().getStatus();
                // 第一审核节点审核
                if (ticketFsm.getTicket().getCurrentAuditNode() == 1) {
                    if (Objects.nonNull(param.getIsAgreed()) && param.getIsAgreed()) {
                        status = TicketStatusEnum.CLOSED.getIndex();
                    } else {
                        if (Objects.nonNull(param.getAuditHandle())) {
                            status = TicketStatusEnum.FOLLOWING.getIndex();
                        } else {
                            status = TicketStatusEnum.CLOSED.getIndex();
                        }
                    }
                    if (Objects.nonNull(ticketFsm.getSecondAuditor()) && ticketRoleManager.hasUser(ticketFsm.getSecondAuditor(), ticketFsm.getTicket().getRegion())) {
                        status = TicketStatusEnum.AUDITING.getIndex();
                    }
                } else if (ticketFsm.getTicket().getCurrentAuditNode() == 2) {
                    // 第二审核点
                    if (Objects.nonNull(param.getIsAgreed()) && param.getIsAgreed()) {
                        status = TicketStatusEnum.CLOSED.getIndex();
                    } else {
                        if (Objects.nonNull(param.getAuditHandle())) {
                            status = TicketStatusEnum.FOLLOWING.getIndex();
                        } else {
                            status = TicketStatusEnum.CLOSED.getIndex();
                        }
                    }
                }
                param.setStatus(TicketStatusEnum.getByIndex(status));
                AuditedTicketRecordParam auditedParam = ticketRecordMapper.paramToAudit(param);

                // 记录工单审核记录
                AuditedTicketRecord audited = ticketRecordManager.audited(auditedParam);

                if (Objects.isNull(ticketFsm.getTicket().getAuditUserIds())) {
                    ticketFsm.getTicket().setAuditUserIds(new ArrayList<>(10));
                }
                if (!ticketFsm.getTicket().getAuditUserIds().contains(audited.getUserId())) {
                    ticketFsm.getTicket().getAuditUserIds().add(audited.getUserId());
                }
                // 第一审核节点审核
                if (ticketFsm.getTicket().getCurrentAuditNode() == 1) {
                    if (Objects.nonNull(ticketFsm.getSecondAuditor()) && ticketRoleManager.hasUser(ticketFsm.getSecondAuditor(), ticketFsm.getTicket().getRegion())) {
                        // 第二节点审核
                        this.dispatchAuditor(ticketFsm, 2);
                    } else {
                        if (Objects.nonNull(audited.getIsAgreed()) && audited.getIsAgreed()) {
                            if (TicketStatusEnum.CLOSED.getIndex().equals(status)) {
                                param.setForbiddenPublish(true);
                            }
                            SystemTicketRecordParam ticketRecordParam = ticketRecordMapper.paramToSystem(param);
                            this.manualClose(ticketRecordParam, ticketFsm);
                        } else {
                            if (Objects.nonNull(audited.getAuditHandle())) {
                                //  转派跟进
                                this.transferFollower(ticketFsm, param);
                            } else {
                                if (TicketStatusEnum.CLOSED.getIndex().equals(status)) {
                                    param.setForbiddenPublish(true);
                                }
                                SystemTicketRecordParam ticketRecordParam = ticketRecordMapper.paramToSystem(param);
                                this.manualClose(ticketRecordParam, ticketFsm);
                            }
                        }
                    }
                } else if (ticketFsm.getTicket().getCurrentAuditNode() == 2) {
                    // 第二审核点
                    if (Objects.nonNull(audited.getIsAgreed()) && audited.getIsAgreed()) {
                        if (TicketStatusEnum.CLOSED.getIndex().equals(status)) {
                            param.setForbiddenPublish(true);
                        }
                        SystemTicketRecordParam ticketRecordParam = ticketRecordMapper.paramToSystem(param);
                        // 审核通过, 手工关闭工单
                        this.manualClose(ticketRecordParam, ticketFsm);
                    } else {
                        if (Objects.nonNull(audited.getAuditHandle())) {
                            this.transferFollower(ticketFsm, param);
                        } else {
                            param.setForbiddenPublish(true);
                            SystemTicketRecordParam ticketRecordParam = ticketRecordMapper.paramToSystem(param);
                            // 手工关闭工单
                            this.manualClose(ticketRecordParam, ticketFsm);
                        }
                    }
                }
                break;
            case AUDIT_OVERDUE_REMIND:
                // 审核逾期前提醒事件
                ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentAuditor(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));
                break;
            case AUDIT_OVERDUE:
                // 审核已逾期提醒事件
                if (Objects.isNull(ticketFsm.getTicket().getHasAuditOverdue()) || !ticketFsm.getTicket().getHasAuditOverdue()) {
                    ticketFsm.getTicket().setHasAuditOverdue(true);
                    SystemTicketRecordParam ticketRecordParam = new SystemTicketRecordParam();
                    ticketRecordParam.setOrgId(ticketFsm.getTicket().getOrgId());
                    ticketRecordParam.setTicketId(ticketFsm.getTicket().getId());
                    ticketRecordManager.system(TicketEventEnum.FOLLOW_OVERDUE_REMIND, ticketRecordParam, ticketFsm.getTicket());
                    ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentAuditor(), TicketEventEnum.getByIndex(ticketFsm.getEvent()));

                    if (ticketFsm.getTicket().getCurrentAuditNode() == 1) {
                        if (TicketAuditor.AUDIT_OVERDUE_PASS.equals(ticketFsm.getFirstAuditor().getOverdueHandle())) {
                            // 逾期自动通过
                            this.systemClosed(ticketRecordParam, ticketFsm);
                        } else if (TicketAuditor.AUDIT_OVERDUE_WAIT.equals(ticketFsm.getFirstAuditor().getOverdueHandle())) {
                            // 逾期继续等待
                            if (log.isInfoEnabled()) {
                                log.info("[Ticket] 工单逾期继续等待，ticket:{}", ticketFsm);
                            }
                        }
                    } else if (ticketFsm.getTicket().getCurrentAuditNode() == 2) {
                        //  第二审核节点逾期
                        if (log.isInfoEnabled()) {
                            log.info("[Ticket] 第二审核节点逾期，ticket:{}", ticketFsm);
                        }
                    }
                }
                break;
            default:
                if (log.isInfoEnabled()) {
                    log.info("[Ticket] AUDITING: {} 进入审核状态失败， 触发事件: {}", ticketFsm.getTicket().getId(), event);
                }
                break;
        }
        return ServiceResult.success();
    }


    /**
     * 转派跟进工单
     *
     * @param ticketFsm
     * @param param
     */
    private void transferFollower(TicketFsm ticketFsm, TransferParam param) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] transferFollower: {}跟进转派第 {}跟进人", ticketFsm.getTicket().getId(),
                    ticketFsm.getTicket().getCurrentFollowNode());
        }
        ticketFsm.getTicket().setStatus(TicketStatusEnum.FOLLOWING.getIndex());
        //     ticket 对象没有follow_node属性 这行先注释掉    self.ticket.follow_node = follow_node
        //        ticketFsm.getTicket().setCurrentFollowNode();

        int timeType = Optional.ofNullable(param.getTimeType()).orElse(0);
        int timeLimit = Optional.ofNullable(param.getTimeLimit()).orElse(0);

        // 按小时逾期
        if (RoleTimeLimitTypeEnum.HOUR.getIndex() == timeType) {
            Date limitTime = Date.from(LocalDateTime.now().plusHours(timeLimit).atZone(UTC_ZONE).toInstant());
            ticketFsm.getTicket().setFollowDeadline(limitTime);
            ticketFsm.getTicket().setTicketDeadline(limitTime);
        }
        // 按天逾期
        if (RoleTimeLimitTypeEnum.DAY.getIndex() == timeType) {
            Date limitTime = Date.from(LocalDateTime.now().plusDays(timeLimit).atZone(UTC_ZONE).toInstant());
            ticketFsm.getTicket().setFollowDeadline(limitTime);
            ticketFsm.getTicket().setTicketDeadline(limitTime);
        }

        // 转派通知, self.notifier.notifyMsg(self.current_follower, TicketEvent.TRANSFER_FOLLOW)
        ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentFollower(), TicketEventEnum.TRANSFER_FOLLOW);
    }


    /**
     * 分配审核角色
     *
     * @param auditNode 审核节点
     */
    private void dispatchAuditor(TicketFsm ticketFsm, int auditNode) {
        ticketFsm.getTicket().setStatus(TicketStatusEnum.AUDITING.getIndex());
        ticketFsm.getTicket().setCurrentFollowNode(auditNode);
        if (auditNode == 1) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] AUDIT: {}分配第一审核节点 审核角色id: {}", ticketFsm.getTicket().getId(),
                        ticketFsm.getFirstAuditor().getRoleId());
            }
            // 分配给第一审核节点
            ticketFsm.getTicket().setAuditRoleId(ticketFsm.getFirstAuditor().getRoleId());

            ticketFsm.getTicket().setAuditDeadline(ticketFsm.getFirstAuditor().getDeadline());
            ticketFsm.getTicket().setTicketDeadline(ticketFsm.getTicket().getAuditDeadline());
            if (log.isInfoEnabled()) {
                log.info("[Ticket] AUDIT: {} 第一审核节点 跟进截止日期: {}", ticketFsm.getTicket().getId(),
                        ticketFsm.getFirstAuditor().getDeadline());
            }
        } else if (auditNode == 2) {
            if (log.isInfoEnabled()) {
                log.info("[Ticket] AUDIT: {}分配第二审核节点 审核角色id: {}", ticketFsm.getTicket().getId(),
                        ticketFsm.getSecondAuditor().getRoleId());
            }
            // 分配给第二审核节点
            ticketFsm.getTicket().setAuditRoleId(ticketFsm.getSecondAuditor().getRoleId());
            ticketFsm.getTicket().setAuditDeadline(ticketFsm.getSecondAuditor().getDeadline());
            ticketFsm.getTicket().setTicketDeadline(ticketFsm.getTicket().getAuditDeadline());

            if (log.isInfoEnabled()) {
                log.info("[Ticket] AUDIT: {} 第二审核节点 跟进截止日期: {}", ticketFsm.getTicket().getId(),
                        ticketFsm.getSecondAuditor().getDeadline());
            }
        }
        // self._add_ticket_audit_remind_task()
        // 添加审核逾期提醒通知消息
        this.addTicketAuditRemindTask(ticketFsm);
        // self._add_ticket_audit_overdue_task()
        // 添加审核逾期通知消息
        this.addTicketAuditOverdueTask(ticketFsm);
        // 通知审核节点
        ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), ticketFsm.currentAuditor(), TicketEventEnum.AUDITING);

    }


    /**
     * 工单全局 分配跟进角色
     *
     * @param followNode 跟进节点
     * @param event      事件
     * @param instance   工单流程信息对象
     * @author LiHeng <heng.li@idiaoyan.com>
     * @author XiaoYunSong <ys.xiao@idiaoyan.com> 2021/12/31 14:42 工单对接钉钉需求, 工单转派后, 清空逾期时间
     * @date 2021/08/24 14:42
     */
    private void dispatchFollower(int followNode, TicketEventEnum event, TicketFsm instance) {
        instance.getTicket().setStatus(TicketStatusEnum.FOLLOWING.getIndex());
        instance.getTicket().setCurrentFollowNode(followNode);
        if (log.isInfoEnabled()) {
            log.info("[Ticket] FOLLOW: {} 分配第一跟进节点 跟进角色id: {}", instance.getTicket().getId(), instance.getFirstFollower().getRoleId());
        }

        if (followNode == 1) {
            // 工单分配给第一跟进人
            // 设置按照角色跟进时，跟进的角色id
            instance.getTicket().setFollowRoleId(StringUtils.isBlank(instance.getFirstFollower().getRoleId()) ? null
                    : instance.getFirstFollower().getRoleId());
            // 设置按照用户跟进时跟进用户的id
            instance.getTicket().setCurFollowUserId(this.getCurrentFollowUser(instance));
            // 更新工单逾期时间
            if (Objects.nonNull(instance.getFirstFollower()) && Objects.nonNull(instance.getFirstFollower().getDeadline())) {
                instance.getTicket().setFollowDeadline(instance.getFirstFollower().getDeadline());
            }
            if (TicketEventEnum.TRANSFER.equals(event)) {
                // 转派 (允许清除工单逾期时间)
                if (Objects.nonNull(instance.getFirstFollower())) {
                    instance.getTicket().setFollowDeadline(instance.getFirstFollower().getDeadline());
                }
            }

            instance.getTicket().setTicketDeadline(instance.getTicket().getFollowDeadline());
            if (log.isInfoEnabled()) {
                log.info("[Ticket] dispatchFollower: {}  第一跟进人跟进截止日期: {}", instance.getTicket().getId(), instance.getFirstFollower().getDeadline());
            }
        } else if (followNode == 2) {
            // 工单分配给第二跟进人
            if (log.isInfoEnabled()) {
                log.info("[Ticket] dispatchFollower: {} 分配第二跟进节点 跟进角色id: {}", instance.getTicket().getId(), instance.getSecondFollower().getRoleId());
            }
            // 设置按照角色跟进时，跟进的角色id
            instance.getTicket().setFollowRoleId(StringUtils.isBlank(instance.getFirstFollower().getRoleId()) ? null
                    : instance.getFirstFollower().getRoleId());
            instance.getTicket().setFollowDeadline(instance.getSecondFollower().getDeadline());
            instance.getTicket().setTicketDeadline(instance.getTicket().getFollowDeadline());
            instance.getTicket().setCurFollowUserId(this.getCurrentFollowUser(instance));
            if (log.isInfoEnabled()) {
                log.info("[Ticket] dispatchFollower: {} 第二跟进人跟进截止日期: {}", instance.getTicket().getId(), instance.getSecondFollower().getDeadline());
            }
            // 保存工单，解决第二跟进人逾期提醒发送错误问题
            ticketDao.overrideUpdateTicket(instance.getTicket());
        } else {
            log.error("[Ticket]  dispatchFollower: 跟进节点错误，跟进节点：{}", followNode);
            return;
        }

        // 如果工单是手动创建或者被转派，则不添加跟进逾期时间，也不发送跟进逾期提醒消息
        if (!TicketEventEnum.TRANSFER.equals(event) && !TicketEventEnum.USER_CREATED.equals(event)) {
            // 添加 工单跟进提醒 延时队列
            addTicketFollowRemindTask(instance);
            // 添加 工单跟进逾期 延时队列
            addTicketFollowOverdueTask(instance);
        }

        // 发送消息通知
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单 dispatchFollower 发送消息; {}; {}; {}", instance.getTicket(), instance.currentFollower(), TicketEventEnum.FOLLOWING);
        }
        ticketNotifyUtil.notifyMsg(instance.getTicket(), instance.currentFollower(), TicketEventEnum.FOLLOWING);
    }


    /**
     * 获取当前跟进用户id
     *
     * @param ticketFsm 工单信息对象
     * @return 当前跟进用户id list
     */
    private List<String> getCurrentFollowUser(TicketFsm ticketFsm) {
        ArrayList<String> userIds = new ArrayList<>(10);
        if (FollowerFilterEnum.USER.equals(ticketFsm.currentFollower().getFollowerFilter()) && StringUtils.isNotBlank(ticketFsm.currentFollower().getFollowerId())) {
            String followerId = ticketFsm.currentFollower().getFollowerId();
            // 获取用户id
            List<UserDTO> userIsHeadquarters = ticketTask.getUserIsHeadquarters(Collections.singletonList(followerId));
            for (UserDTO userIsHeadquarter : userIsHeadquarters) {
                userIds.add(userIsHeadquarter.getId());
            }
        }
        return userIds;

    }


    /**
     * 手动关闭工单
     *
     * @param ticketRecordParam
     * @param ticketFsm
     */
    private void manualClose(SystemTicketRecordParam ticketRecordParam, TicketFsm ticketFsm) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] CLOSED: {} 手工关闭", ticketFsm.getTicket().getId());
        }
        // 关闭工单时间
        ticketFsm.getTicket().setClosedDate(getMongoDate(new Date()));
        // 工单转为手工关闭状态
        ticketFsm.getTicket().setStatus(TicketStatusEnum.CLOSED.getIndex());
        ticketFsm.getTicket().setTicketDeadline(null);
        ticketRecordParam.setStatus(TicketStatusEnum.CLOSED);
        // 创建工单关闭记录
        ticketRecordManager.system(TicketEventEnum.MANUAL_CLOSED, ticketRecordParam, ticketFsm.getTicket());

    }


    /**
     * 系统关闭
     *
     * @param param     通知处理参数
     * @param ticketFsm 工单信息类
     */
    private void systemClosed(SystemTicketRecordParam param, TicketFsm ticketFsm) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] CLOSED: {} 系统关闭", ticketFsm.getTicket().getId());
        }
        ticketFsm.getTicket().setClosedDate(new Date());
        ticketFsm.getTicket().setStatus(TicketStatusEnum.CLOSED.getIndex());
        ticketFsm.getTicket().setTicketDeadline(null);
        param.setStatus(TicketStatusEnum.CLOSED);
        ticketRecordManager.system(TicketEventEnum.SYSTEM_CLOSED, param, ticketFsm.getTicket());
    }


    /**
     * 无需跟进处理
     *
     * @param param     通知处理参数
     * @param ticketFsm 工单信息类
     */
    private void unNeedDeal(SystemTicketRecordParam param, TicketFsm ticketFsm) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] UNNEED_DEAL: {} 无需跟进处理", param.getTicketId());
        }
        // 工单转为仅通知状态
        ticketFsm.getTicket().setStatus(TicketStatusEnum.UNNEED_DEAL.getIndex());
        ticketFsm.getTicket().setTicketDeadline(null);
        param.setStatus(TicketStatusEnum.UNNEED_DEAL);
        // 调用notifier，self.notifier.only_notify(TicketEvent.UNDEED_DEAL)
        ticketNotifyUtil.notifyMsg(ticketFsm.getTicket(), null, TicketEventEnum.UNDEED_DEAL);
        // 创建工单关闭记录
        // await self.recorder.system(TicketEvent.SYSTEM_CLOSED, **kwargs)  # 创建工单关闭记录
    }


    /**
     * 添加工单跟进提醒异步任务
     *
     * @param ticketFsm
     */
    private void addTicketFollowRemindTask(TicketFsm ticketFsm) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单添加 工单跟进提醒 延时队列; ticketId {}", ticketFsm.getTicket().getId());
        }

        // 获取提醒时间
        String ticketConfigId = ticketFsm.getTicket().getTicketConfigId();
        if (Objects.isNull(ticketConfigId) || !ObjectId.isValid(ticketConfigId)) {
            // 无可用ticketConfigId，日志记录
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 添加工单跟进提醒延迟消息失败，ticketConfigId为空。ticketId：{}", ticketFsm.getTicket().getId());
            }
            return;
        }

        TicketNotifyConfig notifyConfig = ticketNotifyConfigDao.findNotifyConfigByIdEvent(ticketConfigId, TicketNotifyConfigEventEnum.FOLLOW_REMIND.getIndex());
        if (Objects.isNull(notifyConfig)) {
            // 无可用 notifyConfig 日志记录
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 添加工单跟进提醒延迟消息失败，通知配置为空。ticketId：{},ticketConfigId:{}",
                        ticketFsm.getTicket().getId(), ticketConfigId);
            }
            return;
        }
        if (!TicketNotifyConfigStatusEnum.ENABLED.getIndex().equals(notifyConfig.getStatus()) || Objects.isNull(notifyConfig.getHoursLimit())) {
            // TicketNotifyConfig 的status为DISABLED，或者notifyConfig.getHoursLimit()为空
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 添加工单跟进提醒延迟消息失败，status为DISABLED或notifyConfig.getHoursLimit()为空。" +
                                "ticketId：{},ticketConfigId:{}，TicketNotifyConfigStatus：{},ticketConfigId。hoursLimit:{}",
                        ticketFsm.getTicket().getId(), ticketConfigId, notifyConfig.getStatus(), notifyConfig.getHoursLimit());
            }
            return;
        }

        // 发送延迟消息
        Integer hoursLimit = notifyConfig.getHoursLimit();
        Date followDeadline = ticketFsm.getTicket().getFollowDeadline();
        if (log.isInfoEnabled()) {
            log.info("[ticket]: 工单跟进逾期提醒-消息发送，工单跟进逾期时间：{}", followDeadline);
        }
        if (Objects.nonNull(followDeadline)) {

            LocalDateTime of = followDeadline.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
            // 将跟进超时时间减去跟进前逾期提前时间设置
            LocalDateTime localDateTime = of.minusHours(hoursLimit);
            Date sendTime = Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
            // 逾期提醒时间定制
     /*       if (OVERDUE_MODIFY_FLAG.equals(ticketFsm.getTicket().getOrgId())) {
                localDateTime = LocalDateTime.now(BEIJING_ZONE).plusMinutes(5);
                if (log.isInfoEnabled()) {
                    log.info("[ticket]: ，发送时间《utc格式》提醒定制of：{}", localDateTime);
                }
                sendTime = Date.from(localDateTime.toInstant(UTC_ZONE));
                if (log.isInfoEnabled()) {
                    log.info("[ticket]: ，发送时间《utc格式》提醒定制：{}", sendTime);
                }
            }*/
            FollowOverdueDTO followOverdueDTO = new FollowOverdueDTO(ticketFsm.getTicket().getId(), TicketEventEnum.FOLLOW_OVERDUE_REMIND.getIndex());
            if (log.isInfoEnabled()) {
                log.info("[ticket]: 单跟进逾期提醒-消息发送，发送时间：{},发送消息内容：{}", sendTime, followOverdueDTO);
            }
            if (localDateTime.isAfter(LocalDateTime.now(ZoneId.systemDefault()))) {
                // 发送 跟进提醒 提醒延迟消息
//                ticketDelayMqSender.sendOverdueDelayMq(followOverdueDTO, sendTime, "跟进提醒");
                ticketMessageService.ticketOverDueSend(followOverdueDTO, sendTime, "跟进提醒");

            } else {
                if (log.isInfoEnabled()) {
                    log.error("[ticket]: 单跟进逾期提醒-消息消息发送失败，发送时间超过当前时间，发送时间：{},发送消息内容：{},发送时间：{}", sendTime, followOverdueDTO,
                            LocalDateTime.now(ZoneId.systemDefault()));
                }
            }
        }
    }


    /**
     * 添加工单跟进逾期异步任务
     *
     * @param ticketFsm
     */
    private void addTicketFollowOverdueTask(TicketFsm ticketFsm) {
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单添加 工单跟进逾期 延时队列; ticketId：{}", ticketFsm.getTicket().getId());
        }

        Date followDeadline = ticketFsm.getTicket().getFollowDeadline();
        String ticketConfigId = ticketFsm.getTicket().getTicketConfigId();
        if (log.isInfoEnabled()) {
            log.info("[ticket]: 工单跟进逾期提醒，ticketId：{}，followDeadline：{}", ticketFsm.getTicket().getId(), followDeadline);
        }
        if (Objects.nonNull(followDeadline)) {
            TicketNotifyConfig notifyConfig = ticketNotifyConfigDao.findNotifyConfigByIdEvent(ticketConfigId, TicketNotifyConfigEventEnum.FOLLOW_OVERDUE.getIndex());
            if (Objects.nonNull(notifyConfig) && TicketNotifyConfigStatusEnum.ENABLED.getIndex().equals(notifyConfig.getStatus())) {
                LocalDateTime of = LocalDateTime.ofInstant(followDeadline.toInstant(), BEIJING_ZONE);
                followDeadline = Date.from(of.toInstant(BEIJING_ZONE));
                // 逾期时间定制
         /*       if (OVERDUE_MODIFY_FLAG.equals(ticketFsm.getTicket().getOrgId())) {
                    of = LocalDateTime.now(BEIJING_ZONE).plusMinutes(5);
                    if (log.isInfoEnabled()) {
                        log.info("[ticket]: 单跟进逾期提醒-消息发送，发送时间《utc格式》逾期定制of：{}", of);
                    }
                    followDeadline = Date.from(of.toInstant(UTC_ZONE));
                    if (log.isInfoEnabled()) {
                        log.info("[ticket]: 单跟进逾期提醒-消息发送，发送时间《utc格式》逾期定制：{}", followDeadline);
                    }
                }*/
                // 本身就是utc时间，followDeadline
                FollowOverdueDTO followOverdueDTO = new FollowOverdueDTO(ticketFsm.getTicket().getId(), TicketEventEnum.FOLLOW_OVERDUE.getIndex());
                if (log.isInfoEnabled()) {
                    log.info("[ticket]: 工单跟进逾期提醒发送延迟队列消息，ticketId：{}，followDeadline：{}，发送消息内容：{}", ticketFsm.getTicket().getId(), followDeadline, followOverdueDTO);
                }
                // 发送 跟进逾期 提醒延迟消息
//                ticketDelayMqSender.sendOverdueDelayMq(followOverdueDTO, followDeadline, "跟进逾期");
                ticketMessageService.ticketOverDueSend(followOverdueDTO, followDeadline, "跟进逾期");

            }
        }
    }


    /**
     * 添加工单审核提醒异步任务
     *
     * @param ticketFsm
     */
    private void addTicketAuditRemindTask(TicketFsm ticketFsm) {
        String ticketConfigId = ticketFsm.getTicket().getTicketConfigId();
        if (Objects.isNull(ticketConfigId) || !ObjectId.isValid(ticketConfigId)) {
            // 无可用ticketConfigId，日志记录
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 工单审核提醒延迟消息失败，ticketConfigId为空。ticketId：{}", ticketFsm.getTicket().getId());
            }
            return;
        }

        TicketNotifyConfig notifyConfig = ticketNotifyConfigDao.findNotifyConfigByIdEvent(ticketConfigId, TicketNotifyConfigEventEnum.AUDIT_REMIND.getIndex());
        if (Objects.isNull(notifyConfig)) {
            // 无可用 notifyConfig 日志记录
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 工单审核提醒延迟消息失败，通知配置为空。ticketId：{},ticketConfigId:{}",
                        ticketFsm.getTicket().getId(),
                        ticketConfigId);
            }
            return;
        }
        if (!TicketNotifyConfigStatusEnum.ENABLED.getIndex().equals(notifyConfig.getStatus()) || Objects.isNull(notifyConfig.getHoursLimit())) {
            // TicketNotifyConfig 的status为DISABLED，或者notifyConfig.getHoursLimit()为空
            if (log.isInfoEnabled()) {
                log.info("[Ticket] 工单审核提醒延迟消息失败，status为DISABLED或notifyConfig.getHoursLimit()为空。" +
                                "ticketId：{},ticketConfigId:{}，TicketNotifyConfigStatus：{},ticketConfigId。hoursLimit:{}",
                        ticketFsm.getTicket().getId(), ticketConfigId, notifyConfig.getStatus(), notifyConfig.getHoursLimit());
            }
            return;
        }

        // 发送延迟消息
        Integer hoursLimit = notifyConfig.getHoursLimit();
        Date auditDeadline = ticketFsm.getTicket().getAuditDeadline();
        if (Objects.nonNull(auditDeadline)) {
            // 转为utc时区时间
            LocalDateTime localDateTime = LocalDateTime.ofInstant(auditDeadline.toInstant(), UTC_ZONE);
            // 将跟进超时时间减去跟进前逾期提前时间设置
            LocalDateTime resultTime = localDateTime.minusHours(hoursLimit);

            FollowOverdueDTO followOverdueDTO = new FollowOverdueDTO(ticketFsm.getTicket().getId(), TicketEventEnum.AUDIT_OVERDUE.getIndex());

            // 转换成utc时间

            if (resultTime.compareTo(LocalDateTime.now()) > 0) {
                // 发送 审核提醒 提醒延迟消息
                Date sendTime = Date.from(resultTime.toInstant(UTC_ZONE));
//                ticketDelayMqSender.sendOverdueDelayMq(followOverdueDTO, sendTime, "审核提醒");
                ticketMessageService.ticketOverDueSend(followOverdueDTO, auditDeadline, "审核提醒");


            }
        }
    }


    /**
     * 添加工单审核逾期异步任务
     *
     * @param ticketFsm
     */
    private void addTicketAuditOverdueTask(TicketFsm ticketFsm) {
        Date auditDeadline = ticketFsm.getTicket().getAuditDeadline();
        String ticketConfigId = ticketFsm.getTicket().getTicketConfigId();
        if (Objects.nonNull(auditDeadline)) {
            TicketNotifyConfig notifyConfig = ticketNotifyConfigDao.findNotifyConfigByIdEvent(ticketConfigId, TicketNotifyConfigEventEnum.AUDIT_OVERDUE.getIndex());
            if (Objects.nonNull(notifyConfig) && TicketNotifyConfigStatusEnum.ENABLED.getIndex().equals(notifyConfig.getStatus())) {
                FollowOverdueDTO followOverdueDTO = new FollowOverdueDTO(ticketFsm.getTicket().getId(), TicketEventEnum.AUDIT_OVERDUE.getIndex());
                // 发送 审核逾期 提醒延迟消息
//                ticketDelayMqSender.sendOverdueDelayMq(followOverdueDTO, auditDeadline, "审核逾期");
                ticketMessageService.ticketOverDueSend(followOverdueDTO, auditDeadline, "审核逾期");
            }
        }
    }
}
